﻿using System.Collections.Generic;
using Entitas;
using Entitas.CodeGeneration.Attributes;
using UnityEngine;

public class Cell
{
    public List<GameEntity> members;
    public AABB aabbBox;

    public Cell(Vector3 min, Vector3 max)
    {
        aabbBox = new AABB(min, max);
        members = new List<GameEntity>();
    }
}

public class CellSpace
{
    private readonly List<Cell> _cells;
    private Vector2Int _cellNum;
    private Vector2 _cellSize;
    private Vector2 _spaceSize;

    public CellSpace(Vector2 spaceSize, Vector2Int cellNum)
    {
        _spaceSize = spaceSize;
        _cellNum = cellNum;
        _cellSize = spaceSize;
        _cellSize.x /= cellNum.x;
        _cellSize.y /= cellNum.y;

        Vector2 center = spaceSize / 2;
        _cells = new List<Cell>();
        for (int y = 0; y < cellNum.y; y++)
        {
            for (int x = 0; x < cellNum.x; x++)
            {
                Vector3 min = new Vector3(x * _cellSize.x - center.x, 0, y * _cellSize.y - center.y);
                Vector3 max = new Vector3((x + 1) * _cellSize.x - center.x, 0, (y + 1) * _cellSize.y - center.y);

                _cells.Add(new Cell(min, max));
            }
        }
    }

    public List<Cell> GetCells()
    {
        return _cells;
    }
    
    public List<GameEntity> GetNeighbors(Vector3 targetPos, float queryRadius)
    {
        List<GameEntity> neighbors = new List<GameEntity>();
        Vector3 q = new Vector3(queryRadius, 0, queryRadius);
        AABB queryBox = new AABB(targetPos - q, targetPos + q);
        foreach (var cell in _cells)
        {
            if (cell.aabbBox.IsIntersect(queryBox))
            {
                foreach (var member in cell.members)
                {
                    if (!member.hasTransform || !member.hasVelocity)
                    {
                        continue;
                    }

                    if ((member.transform.position - targetPos).sqrMagnitude >= queryRadius * queryRadius)
                    {
                        continue;
                    }

                    neighbors.Add(member);
                }
            }
        }

        return neighbors;
    }

    public int PositionToIndex(Vector3 pos)
    {
        Vector2 adjustPos = new Vector2(pos.x, pos.z) + (_spaceSize / 2.0f);
        int idx = (int) (_cellNum.x * adjustPos.x / _spaceSize.x) +
                  ((int) ((_cellNum.y) * adjustPos.y / _spaceSize.y) * _cellNum.x);

        if (idx > _cells.Count - 1)
        {
            idx = _cells.Count - 1;
        }

        return idx;
    }

    public void EmptyCells()
    {
        foreach (var cell in _cells)
        {
            cell.members.Clear();
        }
    }

    public void UpdateEntity(GameEntity entity, Vector3 position)
    {
        int idx = PositionToIndex(position);
        _cells[idx].members.Add(entity);
    }
}

[Unique]
public sealed class CellSpaceComponent : IComponent
{
    public CellSpace cellSpace;
}